﻿using Nest;
using Newtonsoft.Json.Linq;
using QPZS.Tecdoc;
using System;
using System.Collections.Generic;
using System.Linq;
using Wicture.DbRESTFul;
using Wicture.DbRESTFul.Rpc.Client;
using Wicture.DbRESTFul.SQL;
using Wicture.EPC.Common;
using Wicture.QPZS.EPC.Models;
using Wicture.QPZS.EPC.Services;
using Wicture.QPZS.EPC.Services.UserAuth.Base;
using Wicture.QPZS.Parts.Models;

namespace Wicture.QPZS.Parts.Repository
{
    public class PartRepository : DbRESTFulRepository
    {
        private const int PageSize = 10;
        private const string Param_ManufacturerId = "manufacturerId";
        private const string Param_SupplierId = "supplierId";

        // HardCode: supplierNbrOfArticles = 1999999999 => 供应商的配件
        // HardCode: 
        private const string SupplierBrandPartsFlag = "1999999999";

        private static string ImageDomain = ConfigurationManager.Settings.Variables["Qiniu.ImageDomain"];

        public object QueryBrandAutoPartSuppliers(JObject param)
        {
            try
            {
                string acronym = param.Value<string>("acronym");
                if (acronym == null) acronym = "";
                int pageIndex = param.Value<int>("pageIndex");
                int pageSize = param.Value<int>("pageSize");
                return ArticleQueryClient.QuerySuppliers(acronym, pageIndex, pageSize).Result;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To QueryBrandAutoPartSuppliers");
                LoggerManager.Logger.LogWithEmail(ex, "Failed To QueryBrandAutoPartSuppliers", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }

        public object QueryBrandAutoPartDetail(JObject param)
        {
            try
            {
                QueryProcessor.Instance.CheckBrandPartCounts(this);
                var partCode = param.Value<string>("partCode")?.Trim();
                uint? supplierId = param.Value<uint>(Param_SupplierId);
                if (supplierId.HasValue)
                {
                    return ArticleQueryClient.QueryDetails(partCode, supplierId.Value).Result;
                }
                else
                {
                    return ArticleQueryClient.QueryDetails(partCode).Result;
                }
            }
            catch (LogicalException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To QueryBrandAutoPartDetail");
                LoggerManager.Logger.LogWithEmail(ex, "Failed To QueryBrandAutoPartDetail", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }

        public object QueryBrandAutoPartArticles(JObject param)
        {
            try
            {
                int supplierId = param.Value<int>(Param_SupplierId);
                int pageIndex = param.Value<int>("pageIndex");
                int pageSize = param.Value<int>("pageSize");
                int max_size = 99;
                if (pageSize > max_size)
                {
                    throw new LogicalException($"每页数量不能超过{max_size}");
                }
                return ArticleQueryClient.QueryArticles(supplierId, pageIndex, pageSize).Result;
            }
            catch (LogicalException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To QueryBrandAutoPartArticles");
                LoggerManager.Logger.LogWithEmail(ex, "Failed To QueryBrandAutoPartArticles", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }

        public object QueryBrandAutoParts(JObject param)
        {
            try
            {
                var partCode = param.Value<string>("partCode")?.Trim();
                string type = param.Value<string>("type");
                int pageIndex = param.Value<int>("pageIndex");
                int? supplierId = param.Value<int>("supplierId");
                int? manufacturerId = param.Value<int>(Param_ManufacturerId);

                using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
                {
                    settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName, ElasticSearchConfig.Config.ElasticSearchPassword)
                    .DefaultIndex("tecdoc")
                    .DefaultMappingFor<Part>(m => m.IndexName("tecdoc"));
                    var client = new ElasticClient(settings);

                    // 根据UI需要，是否需要将广告供应商的商品，从items列表中过滤掉，并放到单独的contracts列表中。
                    var filterBrandPartsToContacts = false;

                    if (type == "0")
                    {
                        filterBrandPartsToContacts = false; // 在Web的品牌件中，通过原厂OE号搜索，不需要过滤到contracts中
                        var ret = GetBrandAutoPartsByOeCode(client, partCode, supplierId, manufacturerId, pageIndex, filterBrandPartsToContacts);
                        return ret;
                    }
                    else if (type == "1")
                    {
                        var ret = GetBrandAutoPartsByArticleCode(client, partCode, supplierId, pageIndex);
                        return ret;
                    }
                    else if (string.IsNullOrEmpty(type))
                    {
                        filterBrandPartsToContacts = true; // 在EPC中，通过OE号搜索，展示配件详情界面，需要过滤到contracts中
                        var ret = GetBrandAutoPartsByOeCode(client, partCode, supplierId, manufacturerId, pageIndex, filterBrandPartsToContacts);
                        if (ret == null)
                        {
                            ret = GetBrandAutoPartsByArticleCode(client, partCode, supplierId, pageIndex);
                        }
                        return ret;
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            catch (LogicalException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To QueryBrandAutoParts");
                LoggerManager.Logger.LogWithEmail(ex, "Failed To QueryBrandAutoParts", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }

        private JObject GetBrandAutoPartsByArticleCode(ElasticClient client, string partCode, int? supplierId, int pageIndex)
        {
            partCode = partCode?.Trim();
            if (string.IsNullOrEmpty(partCode) && (!supplierId.HasValue || supplierId.Value == 0))
            {
                throw new LogicalException("参数错误，partCode为空时supplierId不可为空！");
            }
            var query = new QueryContainer();
            var partCodeQuery = new QueryContainer();
            partCodeQuery = new TermQuery
            {
                Field = "articleCode",
                Value = partCode
            };
            if (!string.IsNullOrEmpty(partCode))
            {
                query = query & partCodeQuery;
            }
            if (supplierId.HasValue && supplierId.Value != 0)
            {
                query = query & new TermQuery { Field = "supplierId", Value = supplierId.Value };
            }
            var ret = SearchBrandPartsFromES(client, query, pageIndex, PageSize);
            if (ret != null && ret.Hits.Count > 0)
            {
                AggregateDictionary filter = null;
                if (!string.IsNullOrEmpty(partCode))
                {
                    filter = GetSupplierFiltersFromES(client, partCodeQuery);
                }
                return BuildBrandAutoPartsResult(ret, pageIndex, filter, null, false);
            }
            query = new QueryContainer();
            partCodeQuery = new MatchQuery
            {
                Field = "prodDesc",
                Query = partCode,
                Fuzziness = Fuzziness.Auto,
                Operator = Operator.And
            };
            query = query && partCodeQuery;
            if (supplierId.HasValue && supplierId.Value != 0)
            {
                query = query & new TermQuery { Field = Param_SupplierId, Value = supplierId.Value };
            }
            ret = SearchBrandPartsFromES(client, query, pageIndex, PageSize);
            var supplierfilter = GetSupplierFiltersFromES(client, partCodeQuery);
            return BuildBrandAutoPartsResult(ret, pageIndex, supplierfilter, null, false);
        }

        private JObject GetBrandAutoPartsByOeCode(ElasticClient client, string partCode, int? supplierId, int? manufacturerId, int pageIndex, bool filterBrandPartsToContacts)
        {
            if (string.IsNullOrEmpty(partCode))
            {
                throw new LogicalException("参数错误，只有当参数type=1时partCode才可为空!");
            }
            var query = new QueryContainer();
            var partCodeQuery = new QueryContainer();
            var supplierQuery = new QueryContainer();
            partCodeQuery = new TermQuery()
            {
                Field = "oeCode",
                Value = partCode
            };
            query = query && partCodeQuery;
            supplierQuery = supplierQuery && partCodeQuery;
            if (supplierId.HasValue && supplierId != 0)
            {
                var termQuery = new TermQuery { Field = Param_SupplierId, Value = supplierId.Value };
                query = query & termQuery;
            }
            if (manufacturerId.HasValue && manufacturerId != 0)
            {
                var termQuery = new TermQuery { Field = Param_ManufacturerId, Value = manufacturerId.Value };
                query = query & termQuery;
                supplierQuery = supplierQuery && termQuery;
            }
            var ret = SearchBrandPartsFromES(client, query, pageIndex, PageSize);
            if (ret == null || ret.Hits.Count == 0)
            {
                return null;
            }
            var supplierFilter = GetSupplierFiltersFromES(client, supplierQuery);
            var manufacturerFilter = GetManufacturerFiltersFromES(client, partCodeQuery);
            return BuildBrandAutoPartsResult(ret, pageIndex, supplierFilter, manufacturerFilter, filterBrandPartsToContacts);
        }

        private JObject BuildBrandAutoPartsResult(ISearchResponse<BrandPart> ret, int pageIndex, AggregateDictionary supplierAgg, AggregateDictionary manufacturerAgg, bool filterBrandPartsToContacts)
        {
            if (ret == null || ret.Hits.Count == 0) return null;
            JObject retObject = new JObject();

            retObject["items"] = BuildBrandParts(ret.Documents, filterBrandPartsToContacts);
            var pagination = new { totalCount = (ret.Aggregations.Values.First() as ValueAggregate)?.Value.ToInt(), pageSize = PageSize, pageIndex };
            retObject["pagination"] = JObject.FromObject(pagination);

            if (supplierAgg != null && supplierAgg.Values.Any())
            {
                var a = supplierAgg.Values.First() as BucketAggregate;
                var listA = a.Items.OfType<KeyedBucket<object>>();
                var suppliers = listA.Select(q => new { id = q.Key, count = GetValueAggregate(q.Values)?.Value.ToInt(), name = (BucketAggregate(q.Values).Items.First() as KeyedBucket<object>).Key });
                retObject["suppliers"] = JArray.FromObject(suppliers);
            }
            if (manufacturerAgg != null && manufacturerAgg.Values.Any())
            {
                var b = manufacturerAgg.Values.First() as BucketAggregate;
                var listB = b.Items.OfType<KeyedBucket<object>>();
                var manufacuturers = listB.Select(q => new { id = q.Key, count = GetValueAggregate(q.Values)?.Value.ToInt(), name = (BucketAggregate(q.Values).Items.First() as KeyedBucket<object>).Key });
                retObject["manufacturers"] = JArray.FromObject(manufacuturers);
            }

            // 如果需要将广告供应商的商品从es的搜索结果中移到单独的列表中，先过滤掉广告供应商的商品。
            if (filterBrandPartsToContacts) retObject["contacts"] = BuildBrandPartSupplierContacts(ret.Documents);

            return retObject;
        }

        /// <summary>
        /// 处理供应商联系信息
        /// </summary>
        private JArray BuildBrandPartSupplierContacts(IReadOnlyCollection<BrandPart> documents)
        {
            if (documents.Count == 0) return null;
            JArray result = new JArray();
            // 此处的过滤条件是HardCode的特征值，即 supplierNbrOfArticles = 1999999999。
            foreach (var doc in documents.Where(d => d.supplierNbrOfArticles == SupplierBrandPartsFlag))
            {
                if (string.IsNullOrEmpty(doc.supplierHasNewVersionArticles)
                    || doc.supplierHasNewVersionArticles.Equals("True", StringComparison.InvariantCultureIgnoreCase)) continue;

                try
                {
                    var item = JArray.Parse(doc.supplierHasNewVersionArticles)?.FirstOrDefault();
                    if (item == null) continue;
                    var brandPart = new { doc.articleNumber, doc.prodDesc, doc.articleImageFile, doc.prodNormalDesc, doc.articleAttributes };
                    var contact = result.FirstOrDefault(j => j.Value<string>("Name").Equals(item.Value<string>("Name")));
                    if (contact == null)
                    {
                        item["BrandName"] = doc.supplierName;
                        item["BrandParts"] = new JArray() { JObject.FromObject(brandPart) };
                        result.Add(item);
                    }
                    else
                    {
                        (contact["BrandParts"] as JArray).Add(JObject.FromObject(brandPart));
                    }
                }
                catch (Exception) { }
            }

            return result;
        }

        private ValueAggregate GetValueAggregate(IEnumerable<object> values)
        {
            foreach (var value in values)
            {
                if (value.GetType() == typeof(ValueAggregate))
                {
                    return value as ValueAggregate;
                }
            }
            return null;
        }

        private BucketAggregate BucketAggregate(IEnumerable<object> values)
        {
            foreach (var value in values)
            {
                if (value.GetType() == typeof(BucketAggregate))
                {
                    return value as BucketAggregate;
                }
            }
            return null;
        }

        private JArray BuildBrandParts(IReadOnlyCollection<BrandPart> documents, bool filterBrandPartsToContacts)
        {
            // 如果需要将广告供应商的商品从es的搜索结果中移到单独的列表中，先过滤掉广告供应商的商品。
            // 此处的过滤条件是HardCode的特征值，即 supplierNbrOfArticles = 1999999999。
            //var array = documents.Where(d => !filterBrandPartsToContacts || d.supplierNbrOfArticles != SupplierBrandPartsFlag)
            //    .Select(q => new { q.supplierId, supplierName = q.supplierDesc, supplierLogoUrl = GetSupplierLogoImageUrl(q.supplierId, q.supplierHasNewVersionArticles), q.productId, productName = q.prodDesc, dataSupplierArticleNumber = q.articleCode, attributes = q.articleAttributes, pictureUrl = GetFullArticleImageUrl(q.articleImageFile) });
            var array = documents.Select(q => new { q.supplierId, supplierName = q.supplierDesc, supplierLogoUrl = GetSupplierLogoImageUrl(q.supplierId, q.supplierHasNewVersionArticles), q.productId, productName = q.prodDesc, dataSupplierArticleNumber = q.articleCode, attributes = q.articleAttributes, pictureUrl = GetFullArticleImageUrl(q.articleImageFile) });
            return JArray.FromObject(array);
        }

        private string GetSupplierLogoImageUrl(int supplierId, string addresses)
        {
            // 处理 广告供应商的Logo图片地址，需要从地址字段（HardCode -> supplierHasNewVersionArticles）中提取。
            // supplierHasNewVersionArticles 即 addresses，是一个JArray对象的Json字符串，以"["开头。
            if (addresses.StartsWith("["))
            {
                try
                {
                    return JArray.Parse(addresses)?.FirstOrDefault()?.Value<string>("LogoUrl");
                }
                catch (Exception)
                {
                }
            }

            return $"{ImageDomain}supplier/logo/{supplierId}.png";
        }

        private string GetFullArticleImageUrl(string articleImageUrl)
        {
            if (string.IsNullOrEmpty(articleImageUrl)) return "";

            // 如果图片是以http开头，即图片已经是全路径地址了，不需要再加上域名。
            else if (articleImageUrl.StartsWith("http", StringComparison.InvariantCultureIgnoreCase)) return articleImageUrl;

            return $"{ImageDomain}supplier/{articleImageUrl}";
        }

        public object QueryBrandsFromParts(JObject param)
        {
            var codes = param.Value<string>("codes")?.Trim(); ;
            var keyword = param.Value<string>("keywords")?.Trim();
            if (string.IsNullOrEmpty(codes) && string.IsNullOrEmpty(keyword))
            {
                throw new LogicalException("配件编号和关键字至少输入一项。", 603);
            }
            if (string.IsNullOrEmpty(codes))
            {
                var type = param.Value<string>("type")?.Trim();
                using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
                {
                    settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName, ElasticSearchConfig.Config.ElasticSearchPassword)
                    .DefaultIndex("accessory")
                    .DefaultMappingFor<Part>(m => m.IndexName("accessory"));
                    var client = new ElasticClient(settings);
                    var query = new QueryContainer();
                    IEnumerable<object> retList = null;
                    if (string.IsNullOrEmpty(type) || type == "1")
                    {
                        query = new QueryStringQuery
                        {
                            Fields = new string[] { "partCode" },
                            Query = $"*{keyword}*"
                        };
                        retList = DistinctBrands(client, query);
                        if (retList.Count() > 0)
                        {
                            return retList;
                        }
                    }

                    if (string.IsNullOrEmpty(type) || type == "2")
                    {
                        query = new MatchQuery
                        {
                            Field = "partName",
                            Query = keyword,
                            Fuzziness = Fuzziness.Auto,
                            Operator = Operator.And
                        };
                        retList = DistinctBrands(client, query);
                        if (retList.Count() > 0)
                        {
                            return retList;
                        }
                        var partNameQuery = new QueryStringQuery
                        {
                            Fields = new string[] { "partName" },
                            Query = keyword
                        };
                        retList = DistinctBrands(client, query);
                        if (retList.Count() > 0)
                        {
                            return retList;
                        }
                    }

                    return null;
                }
            }
            else
            {
                using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
                {
                    settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName, ElasticSearchConfig.Config.ElasticSearchPassword)
                    .DefaultIndex("accessory")
                    .DefaultMappingFor<Part>(m => m.IndexName("accessory"));
                    var client = new ElasticClient(settings);
                    var query = new QueryContainer();
                    var partCodes = codes.Split(',');
                    query = new TermsQuery()
                    {
                        Field = "partCode",
                        Terms = partCodes

                    };
                    return DistinctBrands(client, query);
                }
            }
        }


        public object QueryParts(JObject param)
        {
            try
            {
                param.RemoveEmptyValues();
                var codes = param.Value<string>("codes")?.Trim(); ;
                var keyword = param.Value<string>("keywords")?.Trim();
                var brandCode = param.Value<string>("brandCode")?.Trim();

                if (string.IsNullOrEmpty(codes) && string.IsNullOrEmpty(keyword))
                {
                    throw new LogicalException("配件编号和关键字至少输入一项。", 603);
                }
                JObject ret = new JObject();
                ISearchResponse<Part> parts;
                RpcClient rpcClient = new RpcClient();
                if (string.IsNullOrEmpty(codes))
                {
                    UserAuthProcessor.CheckSearchPartTimes(this, brandCode, keyword);
                    var type = param.Value<string>("type")?.Trim();
                    parts = Search(keyword, brandCode, type);
                }
                else
                {
                    var partCodes = codes.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    parts = SearchByPartCodes(partCodes, brandCode);
                    int partCount = 0;
                    if (parts != null && parts.Documents != null)
                    {
                        partCount = parts.Documents.Select(q => q.partCode).Distinct().Count();
                    }
                    UserAuthProcessor.CheckSearhPartCounts(this, partCount, brandCode);
                }
                if (parts == null) return null;
                ret["parts"] = JArray.FromObject(SortPartSearchResult(parts.Documents, codes, keyword));
                ret["hits"] = JArray.FromObject(GetHighLights(parts, keyword));
                UserAuthProcessor.BuildPartSearchResult(this, ret);
                return ret;
            }
            catch (LogicalException)
            {
                throw;
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError(ex, $"Failed To QueryParts");
                LoggerManager.Logger.LogWithEmail(ex, "Failed To QueryParts", CurrentUser, this.HttpContext);
                throw new LogicalException(EpcConsts.NormalErrorMessage);
            }
        }

        private IEnumerable<Part> SortPartSearchResult(IEnumerable<Part> documents, string codes, string keywords)
        {
            if (string.IsNullOrEmpty(codes))
            {
                return documents.Where(q => q.partCode == keywords).Union(documents.Where(q => q.partCode != keywords));
            }
            int sort = 0;
            var parts = codes.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(q => new Part { partCode = q, sort = sort++ });
            var list = (from q in documents
                        join p in parts on q.partCode equals p.partCode
                        orderby p.sort
                        select q);
            return list;
        }

        public ISearchResponse<Part> SearchByPartCodes(string[] partCodes, string brandCode, int index = 0, int size = 999)
        {
            using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
            {
                settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName, ElasticSearchConfig.Config.ElasticSearchPassword)
                .DefaultIndex("accessory")
                .DefaultMappingFor<Part>(m => m.IndexName("accessory"));
                var client = new ElasticClient(settings);
                var query = new QueryContainer();

                query = new TermsQuery()
                {
                    Field = "partCode",
                    Terms = partCodes

                };
                if (!string.IsNullOrEmpty(brandCode))
                {
                    var brandCodeQuery = new TermQuery()
                    {
                        Field = "brandCode",
                        Value = brandCode
                    };

                    query = query && brandCodeQuery;
                }
                var response = QueryPartsFromES(client, query, index, size);
                return response;
            }
        }

        public static ISearchResponse<Part> QueryPartsFromES(ElasticClient client, QueryContainer query, int index, int size)
        {
            return client.Search<Part>(idx => idx
                   .Type("part")
                   .From(size * index)
                   .Size(size)
                   .Query(q => query).Highlight(h => h.PreTags("<b>").PostTags("</b>").Fields(f => f.Field("partCode"), f => f.Field("partName"))));
        }

        public static List<PartSearch> GetHighLights(ISearchResponse<Part> parts, string keyword)
        {
            List<PartSearch> partSearches = new List<PartSearch>();
            foreach (var hit in parts.Hits)
            {
                PartSearch partSearch = new PartSearch() { partCode = hit.Source.partCode };

                partSearch.highLights = new List<PartSearch.HighLight>();
                foreach (var highLight in hit.Highlights)
                {
                    if (!string.IsNullOrEmpty(keyword) && highLight.Value.Field == "partCode")
                    {
                        partSearch.highLights.Add(new PartSearch.HighLight { field = "partCode", texts = new string[] { partSearch.partCode.Replace(keyword, $"<b>{keyword}</b>") } });
                    }
                    else
                    {
                        partSearch.highLights.Add(new PartSearch.HighLight { field = highLight.Value.Field, texts = highLight.Value.Highlights });
                    }

                }
                partSearches.Add(partSearch);
            }
            return partSearches;
        }

        private IEnumerable<object> DistinctBrands(ElasticClient client, QueryContainer query)
        {
            return client.Search<Part>(idx => idx
                   .Type("part")
                   .Size(1000)
                   .Query(q => query)
                   .Collapse(x => new FieldCollapse { Field = "brandCode" })).Hits.Select(q => new { q.Source.brandCode, q.Source.brandName });
        }

        private AggregateDictionary GetSupplierFiltersFromES(ElasticClient client, QueryContainer query)
        {
            var ret = client.Search<BrandPart>(idx => idx
                      .Type("brandPart")
                      .Query(q => query)
                      .Aggregations(ag => ag.Terms("supplier_group", t => t.Field(fd => fd.supplierId).Size(10000).Aggregations(ag1 => ag1.Terms("supplier_desc_group", t1 => t1.Field(fd1 => fd1.supplierDesc).Size(10000)).Cardinality("supplier_cardinality", c => c.Field(cf => cf.articleId)))))).Aggregations;
            return ret;
        }

        private AggregateDictionary GetManufacturerFiltersFromES(ElasticClient client, QueryContainer query)
        {
            var ret = client.Search<BrandPart>(idx => idx
                      .Type("brandPart")
                      .Query(q => query)
                      .Aggregations(ag => ag.Terms("manufacturer_group", t => t.Field(fd => fd.manufacturerId).Size(10000).Aggregations(ag1 => ag1.Terms("manufacturer_desc_group", t1 => t1.Field(fd1 => fd1.manufacturerDesc).Size(10000)).Cardinality("manufacturer_cardinality", c => c.Field(cf => cf.articleId)))))).Aggregations;
            return ret;
        }
        private ISearchResponse<BrandPart> SearchBrandPartsFromES(ElasticClient client, QueryContainer query, int index, int size)
        {
            var ret = client.Search<BrandPart>(idx => idx
                       .Type("brandPart")
                       .Query(q => query)
                       .Collapse(x => new FieldCollapse { Field = "articleId" })
                       .Aggregations(ag => ag.Cardinality("articleCode_count", c => c.Field(cf => cf.articleId)))
                       .Sort(s => s.Descending(f => f.supplierNbrOfArticles))
                       .From(size * (index - 1))
                       .Size(size));
            return ret;
        }


        private ISearchResponse<Part> Search(string keyword, string brandCode, string type, int index = 0, int size = 50)
        {
            using (var settings = new ConnectionSettings(new Uri(ElasticSearchConfig.Config.ElasticSearchUrl)))
            {
                settings.BasicAuthentication(ElasticSearchConfig.Config.ElasticSearchUserName, ElasticSearchConfig.Config.ElasticSearchPassword)
                .DefaultIndex("accessory")
                .DefaultMappingFor<Part>(m => m.IndexName("accessory"));

                var client = new ElasticClient(settings);
                var query = new QueryContainer();
                var brandCodeQuery = new TermQuery()
                {
                    Field = "brandCode",
                    Value = brandCode
                };

                keyword = EsHelper.FilterEsSpecWords(keyword);
                ISearchResponse<Part> response = null;
                if (string.IsNullOrEmpty(type) || type == "1")
                {
                    query = new QueryStringQuery
                    {
                        Fields = new string[] { "partCode" },
                        Query = $"*{keyword}*"
                    };
                    response = QueryPartsFromES(client, string.IsNullOrEmpty(brandCode) ? query : query && brandCodeQuery, index, size);
                    if (response.Hits.Count > 0)
                    {
                        return response;
                    }
                }

                if (string.IsNullOrEmpty(type) || type == "2")
                {
                    query = new MatchQuery
                    {
                        Field = "partName",
                        Query = keyword,
                        Fuzziness = Fuzziness.Auto,
                        Operator = Operator.And
                    };
                    response = QueryPartsFromES(client, string.IsNullOrEmpty(brandCode) ? query : query && brandCodeQuery, index, size);
                    if (response.Hits.Count > 0)
                    {
                        return response;
                    }
                    var partNameQuery = new QueryStringQuery
                    {
                        Fields = new string[] { "partName" },
                        Query = keyword
                    };
                    query = partNameQuery;
                    response = QueryPartsFromES(client, string.IsNullOrEmpty(brandCode) ? query : query && brandCodeQuery, index, size);
                    return response;
                }
                return null;
            }
        }
    }
}
